﻿using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates an account.
    /// </summary>
    public class CreateAccount : CreateAuditable<AccountEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
                yield return new SecurityPolicy()
                    .Rule((ctx, uow) => !uow.Execute(new GetConfigParamValueByKey
                    {
                        Key = Par.In("Core").Of(this).Name("Restricted")
                    }).ResultAs(false));
            }
        }

        /// <summary>
        /// Gets or sets the localization profile identifier.
        /// </summary>
        public int LocalizationProfileID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        public string Email
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the username.
        /// </summary>
        public string Username
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        public string Password
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public AccountFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Email) || !Email.IsEmail())
                return Err.Of<AccountEntity>().NotValid("Email");
            if(String.IsNullOrWhiteSpace(Username))
                return Err.Of<AccountEntity>().NotValid("Username");
            if(String.IsNullOrWhiteSpace(Password))
                return Err.Of<AccountEntity>().NotValid("Password");

            string usernamePattern = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Core").Of<AccountEntity>().Name("UsernamePattern")
            }).Result;
            if(!Username.IsMatch(usernamePattern))
                return Err.Of<AccountEntity>().NotValid("Username");

            string passwordPattern = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Core").Of<AccountEntity>().Name("PasswordPattern")
            }).Result;
            if(!Password.IsMatch(passwordPattern))
                return Err.Of<AccountEntity>().NotValid("Password");

            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out AccountEntity result)
        {
            base.Work(uow, out result);

            bool verificationRequired = !Flags.HasFlag(AccountFlags.Verified) && 
                uow.Execute(new GetConfigParamValueByKey
                {
                    Key = Par.In("Core").Of<AccountEntity>().Name("VerificationRequired")
                }).ResultAs<Boolean>();
            if(verificationRequired)
            {
                uow.Execute(new InitAccountVerification
                {
                    EntityID = result.EntityID,
                });
            }
            else if(!Flags.HasFlag(AccountFlags.Active))
            {
                result = uow.Execute(new ActivateAccount
                {
                    EntityID = result.EntityID,
                }).Result;
            }
        }

        protected override void Construct(IUnitOfWork uow, AccountEntity entity)
        {
            base.Construct(uow, entity);

            int passwordSaltLen = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Core").Of<AccountEntity>().Name("PasswordSalt")
            }).ResultAs(10);
            string passwordSalt = StringHelper.Random(passwordSaltLen);
            string saltedPassword = String.Concat(Password, passwordSalt);
            string passwordHash = CryptoUtility.SHA1Hash(saltedPassword)
                .ToBase64String();
            entity.Guid = Guid.NewGuid();
            entity.LocalizationProfile = uow.Execute(new GetLocalizationProfile
            {
                ThrowsException = true,
                EntityID = LocalizationProfileID
            }).Result;
            entity.Email = Email.TrimToLen(100);
            entity.Username = Username.TrimToLen(100);
            entity.PasswordHash = passwordHash;
            entity.PasswordSalt = passwordSalt;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the account.
    /// </summary>
    public class UpdateAccount : UpdateAuditable<AccountEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .Rule((ctx, uow) => ctx.Account(uow).EntityID == EntityID);
            }
        }

        /// <summary>
        /// Gets or sets the localization profile identifier.
        /// </summary>
        public int LocalizationProfileID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        public string Email
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the username.
        /// </summary>
        public string Username
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public AccountFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Email) || !Email.IsEmail())
                return Err.Of<AccountEntity>().NotValid("Email");
            if(String.IsNullOrWhiteSpace(Username))
                return Err.Of<AccountEntity>().NotValid("Username");

            string usernamePattern = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Core").Of<AccountEntity>().Name("UsernamePattern")
            }).Result;
            if(!Username.IsMatch(usernamePattern))
                return Err.Of<AccountEntity>().NotValid("Username");

            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out AccountEntity result)
        {
            string oldEmail = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result.Email;

            base.Work(uow, out result);

            if(!oldEmail.Equals(result.Email) && uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Core").Of<AccountEntity>().Name("VerificationRequired")
            }).ResultAs<Boolean>())
            {
                uow.Execute(new InitAccountVerification
                {
                    EntityID = result.EntityID,
                });
            }
        }

        protected override void Revise(IUnitOfWork uow, AccountEntity entity)
        {
            base.Revise(uow, entity);

            entity.LocalizationProfile = uow.Execute(new GetLocalizationProfile
            {
                ThrowsException = true,
                EntityID = LocalizationProfileID
            }).Result;
            entity.Email = Email.TrimToLen(100);
            entity.Username = Username.TrimToLen(100);
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the account.
    /// </summary>
    public class DeleteAccount : DeleteEntity<AccountEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
            }
        }

        #endregion

        #region Utilities

        protected override void Destruct(IUnitOfWork uow, AccountEntity entity)
        {
            foreach(var i in entity.Attributes)
            {
                uow.Execute(new DeleteAccountAttribute
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Comments)
            {
                uow.Execute(new DeleteAccountComment
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Roles.ToList())
            {
                uow.Execute(new UnmapAccountFromRole
                {
                    EntityID = entity.EntityID,
                    ChildID = i.EntityID
                });
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the account by identifier.
    /// </summary>
    public class GetAccount : QueryEntityByID<AccountEntity>,
        IApiOperation
    {
        #region Utilities

        protected override IQueryable<AccountEntity> Build(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            query = base.Build(uow, query);
            if(Fetch > FetchLevel.None)
                query = query.Fetch(x => x.LocalizationProfile);
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the account by email.
    /// </summary>
    public class GetAccountByEmail : QueryEntity<AccountEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account email.
        /// </summary>
        public string Email
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<AccountEntity> Build(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            query = base.Build(uow, query);
            string email = StringHelper.EnsureNotNull(Email).TrimToLen(100);
            query = query.Where(x => x.Email.Equals(email));
            if(Fetch > FetchLevel.None)
                query = query.Fetch(x => x.LocalizationProfile);
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the account by guid.
    /// </summary>
    public class GetAccountByGuid : QueryEntity<AccountEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account guid.
        /// </summary>
        public Guid Guid
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<AccountEntity> Build(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            query = base.Build(uow, query);
            query = query.Where(x => x.Guid == Guid);
            if(Fetch > FetchLevel.None)
                query = query.Fetch(x => x.LocalizationProfile);
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the account by username.
    /// </summary>
    public class GetAccountByUsername : QueryEntity<AccountEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account username.
        /// </summary>
        public string Username
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<AccountEntity> Build(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            string username = StringHelper.EnsureNotNull(Username).TrimToLen(100);
            query = base.Build(uow, query).Where(x => x.Username.Equals(username));
            if(Fetch > FetchLevel.None)
                query = query.Fetch(x => x.LocalizationProfile);
            return query;
        }

        #endregion
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over account collection.
    /// </summary>
    public class SearchAccounts : SearchOperation<AccountEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchAccounts()
        {
            Term = SearchTerm.Empty;
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                AccountSortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the role identifier.
        /// </summary>
        public int? RoleID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the localization profile identifier.
        /// </summary>
        public int? LocalizationProfileID
        {
            get;
            set;
        }

        /// <summary>
        ///   Gets or sets the creation date 'from'.
        /// </summary>
        public DateTime? CreatedFrom
        {
            get;
            set;
        }

        /// <summary>
        ///   Gets or sets the creation date 'to'.
        /// </summary>
        public DateTime? CreatedTo
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public AccountFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public AccountSortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<AccountEntity> Build(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            query = query.FilterByDateRange(x => x.CreatedOn, CreatedFrom, CreatedTo);
            if(RoleID.HasValue)
                query = query.Where(x => x.Roles.Any(r => r.EntityID == RoleID.Value));
            if(LocalizationProfileID.HasValue)
                query = query.Where(x => x.LocalizationProfile.EntityID == LocalizationProfileID.Value);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x => x.Email.StartsWith(Term.Keyword) ||
                        x.Username.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x => x.Email.EndsWith(Term.Keyword) ||
                        x.Username.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x => x.Email.Contains(Term.Keyword) ||
                        x.Username.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x => x.Email.Equals(Term.Keyword) ||
                        x.Username.Equals(Term.Keyword));
                    break;
            }
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case AccountSortBy.Email:
                            query = query.OrderBy(x => x.Email, SortDir, i != 0);
                            break;
                        case AccountSortBy.Username:
                            query = query.OrderBy(x => x.Username, SortDir, i != 0);
                            break;
                        case AccountSortBy.CreatedOn:
                            query = query.OrderBy(x => x.CreatedOn, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the collection of accounts as a dictionary, where 
    /// key - account identifier and value - account email.
    /// </summary>
    public class GetAccountDictionary : SearchOperation<AccountEntity, Int32, String>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public GetAccountDictionary()
        {
            Term = SearchTerm.Empty;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public AccountFlags FlagMask
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<AccountEntity> Build(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            query = base.Build(uow, query);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    query = query.Where(x =>
                        x.Email.StartsWith(Term.Keyword) ||
                        x.Username.StartsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    query = query.Where(x =>
                        x.Email.EndsWith(Term.Keyword) ||
                        x.Username.EndsWith(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Contains:
                    query = query.Where(x =>
                        x.Email.Contains(Term.Keyword) ||
                        x.Username.Contains(Term.Keyword));
                    break;
                case SearchTerm.SearchMode.Equals:
                    query = query.Where(x =>
                        x.Email.Equals(Term.Keyword) ||
                        x.Username.Equals(Term.Keyword));
                    break;
            }
            query = query.OrderBy(x => x.Email);
            return query;
        }

        protected override IQueryable<KeyValuePair<Int32, String>> Map(IQueryable<AccountEntity> query)
        {
            return query.Select(x => new KeyValuePair<Int32, String>(x.EntityID, x.Email));
        }

        #endregion
    }

    /// <summary>
    /// Checks, if the account with the specified username exists.
    /// </summary>
    public class CheckAccountByUsername : QueryOperation<AccountEntity, Boolean>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account username.
        /// </summary>
        public string Username
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<AccountEntity> Build(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            string username = StringHelper.EnsureNotNull(Username).TrimToLen(100);
            return base.Build(uow, query).Where(x => x.Username.Equals(username));
        }

        protected override bool Do(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            return query.Any();
        }

        #endregion
    }

    /// <summary>
    /// Checks, if the account with the specified email exists.
    /// </summary>
    public class CheckAccountByEmail : QueryOperation<AccountEntity, Boolean>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account email.
        /// </summary>
        public string Email
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<AccountEntity> Build(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            string email = StringHelper.EnsureNotNull(Email).TrimToLen(100);
            return base.Build(uow, query).Where(x => x.Email.Equals(email));
        }

        protected override bool Do(IUnitOfWork uow, IQueryable<AccountEntity> query)
        {
            return query.Any();
        }

        #endregion
    }

    #endregion

    #region __Map__

    /// <summary>
    /// Maps the account to role.
    /// </summary>
    public class MapAccountToRole : MapOperation<AccountEntity, RoleEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
            }
        }

        #endregion
    }

    /// <summary>
    /// Unmaps the account from role.
    /// </summary>
    public class UnmapAccountFromRole : UnmapOperation<AccountEntity, RoleEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
            }
        }

        #endregion
    }

    #endregion

    #region __Password__

    /// <summary>
    /// Validates the account password.
    /// </summary>
    public class ValidateAccountPassword : OperateEntityByID<Boolean>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        public string Password
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out bool result)
        {
            result = false;
            var account = uow.Execute(new GetAccount
            {
                EntityID = EntityID
            }).Result;
            if(account == null)
                return;
            string saltedPassword = String.Concat(Password, account.PasswordSalt);
            result = CryptoUtility.SHA1Hash(saltedPassword)
                .ToBase64String()
                .Equals(account.PasswordHash);
        }

        #endregion
    }

    /// <summary>
    /// Validates the account password.
    /// </summary>
    public class ValidateAccountPasswordByEmail : Operation<Boolean>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        public string Email
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        public string Password
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out bool result)
        {
            result = false;
            var account = uow.Execute(new GetAccountByEmail
            {
                Email = Email
            }).Result;
            if(account == null)
                return;

            result = uow.Execute(new ValidateAccountPassword
            {
                EntityID = account.EntityID,
                Password = Password
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Changes the account password.
    /// </summary>
    public class ChangeAccountPassword : UpdateAuditable<AccountEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .Rule((ctx, uow) => ctx.Account(uow).EntityID == EntityID);
            }
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        public string Password
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(String.IsNullOrWhiteSpace(Password))
                return Err.Of<AccountEntity>().NotValid("Password");

            string passwordPattern = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Core").Of<AccountEntity>().Name("PasswordPattern")
            }).Result;
            if(!Password.IsMatch(passwordPattern))
                return Err.Of<AccountEntity>().NotValid("Password");

            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out AccountEntity result)
        {
            base.Work(uow, out result);

            try
            {
                uow.Execute(new EnqueueAccountPasswordNotification
                {
                    AccountID = result.EntityID,
                    RecipientID = result.EntityID,
                    Password = Password
                });
            }
            catch(Exception ex)
            {
                uow.Execute(new CreateLogRecordFromException
                {
                    Ex = ex
                });
            }
        }

        protected override void Revise(IUnitOfWork uow, AccountEntity entity)
        {
            base.Revise(uow, entity);

            int passwordSaltLen = uow.Execute(new GetConfigParamValueByKey
            {
                Key = Par.In("Core").Of<AccountEntity>().Name("PasswordSalt")
            }).ResultAs(10);
            string passwordSalt = StringHelper.Random(passwordSaltLen);
            string saltedPassword = String.Concat(Password, passwordSalt);
            string passwordHash = CryptoUtility.SHA1Hash(saltedPassword)
                .ToBase64String();
            entity.PasswordSalt = passwordSalt;
            entity.PasswordHash = passwordHash;
        }

        #endregion
    }

    /// <summary>
    /// Changes the account password.
    /// </summary>
    public class ChangeAccountPasswordByEmail : Operation<AccountEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .Rule((ctx, uow) => ctx.Account(uow).Email.Equals(Email));
            }
        }

        /// <summary>
        /// Gets or sets the email.
        /// </summary>
        public string Email
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        public string Password
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out AccountEntity result)
        {
            result = uow.Execute(new ChangeAccountPassword
            {
                EntityID = uow.Execute(new GetAccountByEmail
                {
                    ThrowsException = true,
                    Email = Email
                }).Result.EntityID,
                Password = Password
            }).Result;
        }

        #endregion
    }

    #endregion

    #region __Activation__

    /// <summary>
    /// Sets an account active flag, sends the 
    /// related notification message.
    /// </summary>
    public class ActivateAccount : UpdateAuditable<AccountEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out AccountEntity result)
        {
            base.Work(uow, out result);
            try
            {
                uow.Execute(new EnqueueAccountNotification
                {
                    AccountID = result.EntityID,
                    RecipientID = result.EntityID
                });
            }
            catch(Exception ex)
            {
                uow.Execute(new CreateLogRecordFromException
                {
                    Ex = ex
                });
            }
        }

        protected override void Revise(IUnitOfWork uow, AccountEntity entity)
        {
            base.Revise(uow, entity);

            entity.Flags |= AccountFlags.Active;
        }

        #endregion
    }

    /// <summary>
    /// Resets an account active flag.
    /// </summary>
    public class DeactivateAccount : UpdateAuditable<AccountEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
            }
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, AccountEntity entity)
        {
            base.Revise(uow, entity);

            entity.Flags &= (~AccountFlags.Active);
        }

        #endregion
    }

    #endregion

    #region __Verification__

    /// <summary>
    /// Resets an account verification flag, generates a new verification code 
    /// and sends the verification message.
    /// </summary>
    public class InitAccountVerification : UpdateAuditable<AccountEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("ACCNT");
            }
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, AccountEntity entity)
        {
            base.Revise(uow, entity);

            entity.Flags &= (~AccountFlags.Verified);
        }

        protected override void Work(IUnitOfWork uow, out AccountEntity result)
        {
            base.Work(uow, out result);

            var verificationCodeAttr = uow.Execute(new SetAccountAttribute
            {
                HolderID = result.EntityID,
                Key = Par.In("Verification").Name("Code"),
                Value = Guid.NewGuid().ToString()
            }).Result;

            try
            {
                uow.Execute(new EnqueueAccountVerficiationInitNotification
                {
                    AccountID = result.EntityID,
                    RecipientID = result.EntityID,
                    VerificationCode = verificationCodeAttr.Value
                });
            }
            catch(Exception ex)
            {
                uow.Execute(new CreateLogRecordFromException
                {
                    Ex = ex
                });
            }

            result = uow.Execute(new DeactivateAccount
            {
                EntityID = result.EntityID
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Uses the previously generated code to commit an 
    /// account verification.
    /// </summary>
    public class CommitAccountVerification : UpdateAuditable<AccountEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the verification code.
        /// </summary>
        public Guid VerificationCode
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            var account = uow.Execute(new GetAccount
            {
                EntityID = EntityID
            }).Result;
            if(account == null)
                return Err.Of<AccountEntity>().NotAvailable();
            var verificationCodeAttr = uow.Execute(new GetAccountAttributeByKey
            {
                HolderID = EntityID,
                Key = Par.In("Verification").Name("Code")
            }).Result;
            if(verificationCodeAttr == null)
                return Err.Of(this).NotPossible();
            var verificationCode = verificationCodeAttr.ValueAs<Guid>();
            if(verificationCode != VerificationCode)
                return Err.Of(this).NotValid("VerificationCode");
            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out AccountEntity result)
        {
            base.Work(uow, out result);

            uow.Execute(new DeleteAccountAttribute
            {
                EntityID = uow.Execute(new GetAccountAttributeByKey
                {
                    HolderID = EntityID,
                    Key = Par.In("Verification").Name("Code")
                }).Result.EntityID
            });

            try
            {
                uow.Execute(new EnqueueAccountVerficiationCommitNotification
                {
                    AccountID = result.EntityID,
                    RecipientID = result.EntityID
                });
            }
            catch(Exception ex)
            {
                uow.Execute(new CreateLogRecordFromException
                {
                    Ex = ex
                });
            }

            result = uow.Execute(new ActivateAccount
            {
                EntityID = result.EntityID
            }).Result;
        }

        protected override void Revise(IUnitOfWork uow, AccountEntity entity)
        {
            base.Revise(uow, entity);

            entity.Flags |= AccountFlags.Verified;
        }

        #endregion
    }

    #endregion

    #region __Notification__

    /// <summary>
    /// Enqueues an account notification message.
    /// </summary>
    /// <remarks>
    /// Message template code: IACC
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid], [T:Recipient.Email], [T:Recipient.Username],
    /// [T:Account.EntityID], [T:Account.Guid], [T:Account.Email], [T:Account.Username]
    /// </remarks>
    public class EnqueueAccountNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account identifier.
        /// </summary>
        public int AccountID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient account identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var account = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = AccountID
            }).Result;
            var recipient = account.EntityID == RecipientID ? account : uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var usrLocale = recipient.LocalizationProfile;

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "IACC",
                LanguageID = usrLocale.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"Account.EntityID", account.EntityID.ToString()},
                    {"Account.Email", account.Email},
                    {"Account.Username", account.Username},
                    {"Account.Guid", account.Guid.ToString()}
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Enqueues an account password notification message.
    /// </summary>
    /// <remarks>
    /// Message template code: NACCPWD
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid], [T:Recipient.Email], [T:Recipient.Username],
    /// [T:Account.EntityID], [T:Account.Guid], [T:Account.Email], [T:Password]
    /// </remarks>
    public class EnqueueAccountPasswordNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account identifier.
        /// </summary>
        public int AccountID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient account identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the plain text password.
        /// </summary>
        public string Password
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var account = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = AccountID
            }).Result;
            var recipient = account.EntityID == RecipientID ? account : uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var usrLocale = recipient.LocalizationProfile;

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "NACCPWD",
                LanguageID = usrLocale.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"Account.EntityID", account.EntityID.ToString()},
                    {"Account.Email", account.Email},
                    {"Account.Username", account.Username},
                    {"Account.Guid", account.Guid.ToString()},
                    {"Password", Password }
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Enqueues an account verification notification init-message.
    /// </summary>
    /// <remarks>
    /// Message template code: RACCVRF
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid], [T:Recipient.Email], [T:Recipient.Username],
    /// [T:Account.EntityID], [T:Account.Guid], [T:Account.Email], [T:VerificationCode]
    /// </remarks>
    public class EnqueueAccountVerficiationInitNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account identifier.
        /// </summary>
        public int AccountID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient account identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the verification code.
        /// </summary>
        public string VerificationCode
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var account = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = AccountID
            }).Result;
            var recipient = account.EntityID == RecipientID ? account : uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var usrLocale = recipient.LocalizationProfile;

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "RACCVRF",
                LanguageID = usrLocale.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"Account.EntityID", account.EntityID.ToString()},
                    {"Account.Email", account.Email},
                    {"Account.Username", account.Username},
                    {"Account.Guid", account.Guid.ToString()},
                    {"VerificationCode", VerificationCode}
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Enqueues an account verification notification commit-message.
    /// </summary>
    /// <remarks>
    /// Message template code: CACCVRF
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid], [T:Recipient.Email], [T:Recipient.Username],
    /// [T:Account.EntityID], [T:Account.Guid], [T:Account.Email]
    /// </remarks>
    public class EnqueueAccountVerficiationCommitNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account identifier.
        /// </summary>
        public int AccountID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient account identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var account = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = AccountID
            }).Result;
            var recipient = account.EntityID == RecipientID ? account : uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var usrLocale = recipient.LocalizationProfile;

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "CACCVRF",
                LanguageID = usrLocale.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"Account.EntityID", account.EntityID.ToString()},
                    {"Account.Email", account.Email},
                    {"Account.Username", account.Username},
                    {"Account.Guid", account.Guid.ToString()}
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    #endregion
}
